部署 Wordpress 示例

前面的课程中我们基本上了解了Kubernetes当中常见的一些对象资源,这节课我们就来利用前面学习的知识点来部署一个实际的应用 - 将Wordpress应用部署到我们的集群当中,我们前面是不是已经用docker-compose的方式部署过了,我们可以了解到要部署一个Wordpress应用主要涉及到两个镜像:wordpressmysqlwordpress是应用的核心程序,mysql是用于数据存储的。

现在我们来看看如何来部署我们的这个wordpress应用

一个Pod

我们知道一个Pod中可以包含多个容器,那么很明显我们这里是不是就可以将wordpress部署成一个独立的Pod?我们将我们的应用都部署到blog这个命名空间下面,所以先创建一个命名空间:

  1. $ kubectl create namespace blog
  2. namespace "blog" created

然后来编写YAML文件:(wordpress-pod.yaml)

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: wordpress
  5. namespace: blog
  6. spec:
  7. containers:
  8. - name: wordpress
  9. image: wordpress
  10. ports:
  11. - containerPort: 80
  12. name: wdport
  13. env:
  14. - name: WORDPRESS_DB_HOST
  15. value: localhost: 3306
  16. - name: WORDPRESS_DB_USER
  17. value: wordpress
  18. - name: WORDPRESS_DB_PASSWORD
  19. value: wordpress
  20. - name: mysql
  21. image: mysql:5.7
  22. imagePullPolicy: IfNotPresent
  23. ports:
  24. - containerPort: 3306
  25. name: dbport
  26. env:
  27. - name: MYSQL_ROOT_PASSWORD
  28. value: rootPassW0rd
  29. - name: MYSQL_DATABASE
  30. value: wordpress
  31. - name: MYSQL_USER
  32. value: wordpress
  33. - name: MYSQL_PASSWORD
  34. value: wordpress
  35. volumeMounts:
  36. - name: db
  37. mountPath: /var/lib/mysql
  38. volumes:
  39. - name: db
  40. hostPath:
  41. path: /var/lib/mysql

要注意这里针对mysql这个容器我们做了一个数据卷的挂载,这是为了能够将mysql的数据能够持久化到节点上,这样下次mysql容器重启过后数据不至于丢失。
然后创建上面的Pod:

  1. $ kubectl create -f wrodpress-pod.yaml
  2. pod "wordpress" created

接下来就是等待拉取镜像,启动容器,同样我们可以使用describe指令查看详细信息:

  1. $ kubectl describe pod wordpress -n blog

大家可以看看我们现在这种单一Pod的方式有什么缺点?假如我们现在需要部署3个Wordpress的副本,该怎么办?是不是我们只需要在上面的YAML文件中加上replicas: 3这个属性就可以了啊?但是有个什么问题呢?是不是不仅仅是Wordpress这个容器会被部署成3份,连我们的MySQL数据库也会被部署成3份了呢?MySQL数据库单纯的部署成3份他们能联合起来使用吗?不能,如果真的这么简单的话就不需要各种数据库集群解决方案了,所以我们这里部署3个Pod实例,实际上他们互相之间是独立的,因为数据不想通,明白吧?所以该怎么办?拆分呗,把wordpressmysql这两个容器部署成独立的Pod是不是就可以了。

另外一个问题是我们的wordpress容器需要去连接mysql数据库吧,现在我们这里放在一起能保证mysql先启动起来吗?貌似没有特别的办法,前面学习的InitContainer也是针对Pod来的,所以无论如何,我们都需要将他们进行拆分。

两个Pod

现在来把上面的一个Pod拆分成两个Pod,我们前面也反复强调过要使用Deployment来管理我们的Pod,上面只是为了单纯给大家说明怎么来把前面的Docker环境下的wordpress转换成Kubernetes环境下面的Pod,有了上面的Pod模板,我们现在来转换成Deployment很容易了吧。

第一步,创建一个MySQLDeployment对象:(wordpress-db.yaml)

  1. ---
  2. apiVersion: apps/v1beta1
  3. kind: Deployment
  4. metadata:
  5. name: mysql-deploy
  6. namespace: blog
  7. labels:
  8. app: mysql
  9. spec:
  10. template:
  11. metadata:
  12. labels:
  13. app: mysql
  14. spec:
  15. containers:
  16. - name: mysql
  17. image: mysql:5.7
  18. imagePullPolicy: IfNotPresent
  19. ports:
  20. - containerPort: 3306
  21. name: dbport
  22. env:
  23. - name: MYSQL_ROOT_PASSWORD
  24. value: rootPassW0rd
  25. - name: MYSQL_DATABASE
  26. value: wordpress
  27. - name: MYSQL_USER
  28. value: wordpress
  29. - name: MYSQL_PASSWORD
  30. value: wordpress
  31. volumeMounts:
  32. - name: db
  33. mountPath: /var/lib/mysql
  34. volumes:
  35. - name: db
  36. hostPath:
  37. path: /var/lib/mysql

如果我们只创建上面的Deployment这个对象,那么我们应该怎样让后面的Wordpress来访问呢?貌似没办法是吧,之前在一个Pod里面还可以使用localhost来进行访问,现在分开了该怎样访问呢?还记得前面的Service吗?没错,使用Service就可以了,所以我们在上面的wordpress-db.yaml文件中添加上Service的信息:

  1. ---
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: mysql
  6. namespace: blog
  7. spec:
  8. selector:
  9. app: mysql
  10. ports:
  11. - name: mysqlport
  12. protocol: TCP
  13. port: 3306
  14. targetPort: dbport

然后创建上面的wordpress-db.yaml文件:

  1. $ kubectl create -f wordpress-db.yaml
  2. service "mysql" created
  3. deployment.apps "mysql-deploy" created

然后我们查看Service的详细情况:

  1. $ kubectl describe svc mysql -n blog
  2. Name: mysql
  3. Namespace: blog
  4. Labels: <none>
  5. Annotations: <none>
  6. Selector: app=mysql
  7. Type: ClusterIP
  8. IP: 10.98.27.19
  9. Port: mysqlport 3306/TCP
  10. TargetPort: dbport/TCP
  11. Endpoints: 10.244.2.213:3306
  12. Session Affinity: None
  13. Events: <none>

可以看到Endpoints部分匹配到了一个Pod,生成了一个clusterIP10.98.27.19,现在我们是不是就可以通过这个clusterIP加上定义的3306端口就可以正常访问我们这个mysql服务了。

第二步. 创建Wordpress服务,将上面的wordpressPod转换成Deployment对象:(wordpress.yaml)

  1. apiVersion: apps/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: wordpress-deploy
  5. namespace: blog
  6. labels:
  7. app: wordpress
  8. spec:
  9. template:
  10. metadata:
  11. labels:
  12. app: wordpress
  13. spec:
  14. containers:
  15. - name: wordpress
  16. image: wordpress
  17. imagePullPolicy: IfNotPresent
  18. ports:
  19. - containerPort: 80
  20. name: wdport
  21. env:
  22. - name: WORDPRESS_DB_HOST
  23. value: 10.98.27.19:3306
  24. - name: WORDPRESS_DB_USER
  25. value: wordpress
  26. - name: WORDPRESS_DB_PASSWORD
  27. value: wordpress

注意这里的环境变量WORDPRESS_DB_HOST的值将之前的localhost地址更改成了上面mysql服务的clusterIP地址了,然后创建上面的Deployment对象:

  1. $ kubectl create -f wordpress.yaml
  2. deployment.apps "wordpress-deploy" created

创建完成后,我们可以看看我们创建的Pod的状态:

  1. $ kubectl get pods -n blog
  2. NAME READY STATUS RESTARTS AGE
  3. mysql-deploy-86bdcc7484-fv2dj 1/1 Running 0 19m
  4. wordpress-deploy-784cfd6dd4-d9f52 1/1 Running 0 23s

可以看到都已经是Running状态了,然后我们需要怎么来验证呢?是不是去访问下我们的wordpress服务就可以了,要访问,我们就需要建立一个能让外网用户访问的Service,前面我们学到过是不是NodePort类型的Service就可以?所以在上面的wordpress.yaml文件中添加上Service的信息:

  1. ---
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: wordpress
  6. namespace: blog
  7. spec:
  8. type: NodePort
  9. selector:
  10. app: wordpress
  11. ports:
  12. - name: wordpressport
  13. protocol: TCP
  14. port: 80
  15. targetPort: wdport

注意要添加属性type: NodePort,然后重新更新wordpress.yaml文件:

  1. $ kubectl apply -f wordpress.yaml
  2. service "wordpress" created
  3. Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
  4. deployment.apps "wordpress-deploy" configured

创建完成后,查看下svc

  1. $ kubectl get svc -n blog
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. mysql ClusterIP 10.98.27.19 <none> 3306/TCP 25m
  4. wordpress NodePort 10.101.7.69 <none> 80:32255/TCP 1m

可以看到wordpress服务产生了一个32255的端口,现在我们是不是就可以通过任意节点的NodeIP加上32255端口,就可以访问我们的wordpress应用了,在浏览器中打开,如果看到wordpress跳转到了安装页面,证明我们的嗯安装是没有任何问题的了,如果没有出现预期的效果,那么就需要去查看下Pod的日志来查看问题了:
wordpress

然后根据页面提示,填上对应的信息,点击“安装”即可,最终安装成功后,我们就可以看到熟悉的首页界面了:
wordpress-home

提高稳定性

现在wordpress应用已经部署成功了,那么就万事大吉了吗?如果我们的网站访问量突然变大了怎么办,如果我们要更新我们的镜像该怎么办?如果我们的mysql服务挂掉了怎么办?

所以要保证我们的网站能够非常稳定的提供服务,我们做得还不够,我们可以通过做些什么事情来提高网站的稳定性呢?

第一. 增加健康检测,我们前面说过liveness proberediness probe是提高应用稳定性非常重要的方法:

  1. livenessProbe:
  2. tcpSocket:
  3. port: 80
  4. initialDelaySeconds: 3
  5. periodSeconds: 3
  6. readinessProbe:
  7. tcpSocket:
  8. port: 80
  9. initialDelaySeconds: 5
  10. periodSeconds: 10

增加上面两个探针,每10s检测一次应用是否可读,每3s检测一次应用是否存活

第二. 增加 HPA,让我们的应用能够自动应对流量高峰期:

  1. $ kubectl autoscale deployment wordpress-deploy --cpu-percent=10 --min=1 --max=10 -n blog
  2. deployment "wordpress-deploy" autoscaled

我们用kubectl autoscale命令为我们的wordpress-deploy创建一个HPA对象,最小的 pod 副本数为1,最大为10,HPA会根据设定的 cpu使用率(10%)动态的增加或者减少pod数量。当然最好我们也为Pod声明一些资源限制:

  1. resources:
  2. limits:
  3. cpu: 200m
  4. memory: 200Mi
  5. requests:
  6. cpu: 100m
  7. memory: 100Mi

更新Deployment后,我们可以可以来测试下上面的HPA是否会生效:

  1. $ kubectl run -i --tty load-generator --image=busybox /bin/sh
  2. If you don't see a command prompt, try pressing enter.
  3. / # while true; do wget -q -O- http://10.244.1.62:80; done

观察Deployment的副本数是否有变化

  1. $ kubectl get deployment wordpress-deploy
  2. NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
  3. wordpress-deploy 3 3 3 3 4d

第三. 增加滚动更新策略,这样可以保证我们在更新应用的时候服务不会被中断:

  1. replicas: 2
  2. revisionHistoryLimit: 10
  3. minReadySeconds: 5
  4. strategy:
  5. type: RollingUpdate
  6. rollingUpdate:
  7. maxSurge: 1
  8. maxUnavailable: 1

第四. 我们知道如果mysql服务被重新创建了的话,它的clusterIP非常有可能就变化了,所以上面我们环境变量中的WORDPRESS_DB_HOST的值就会有问题,就会导致访问不了数据库服务了,这个地方我们可以直接使用Service的名称来代替host,这样即使clusterIP变化了,也不会有任何影响,这个我们会在后面的服务发现的章节和大家深入讲解的:

  1. env:
  2. - name: WORDPRESS_DB_HOST
  3. value: mysql:3306

第五. 我们在部署wordpress服务的时候,mysql服务以前启动起来了吗?如果没有启动起来是不是我们也没办法连接数据库了啊?该怎么办,是不是在启动wordpress应用之前应该去检查一下mysql服务,如果服务正常的话我们就开始部署应用了,这是不是就是InitContainer的用法:

  1. initContainers:
  2. - name: init-db
  3. image: busybox
  4. command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql service; sleep 2; done;']

直到mysql服务创建完成后,initContainer才结束,结束完成后我们才开始下面的部署。

最后,我们把部署的应用整合到一个YAML文件中来:(wordpress-all.yaml)

  1. ---
  2. apiVersion: apps/v1beta1
  3. kind: Deployment
  4. metadata:
  5. name: mysql-deploy
  6. namespace: blog
  7. labels:
  8. app: mysql
  9. spec:
  10. template:
  11. metadata:
  12. labels:
  13. app: mysql
  14. spec:
  15. containers:
  16. - name: mysql
  17. image: mysql:5.7
  18. ports:
  19. - containerPort: 3306
  20. name: dbport
  21. env:
  22. - name: MYSQL_ROOT_PASSWORD
  23. value: rootPassW0rd
  24. - name: MYSQL_DATABASE
  25. value: wordpress
  26. - name: MYSQL_USER
  27. value: wordpress
  28. - name: MYSQL_PASSWORD
  29. value: wordpress
  30. volumeMounts:
  31. - name: db
  32. mountPath: /var/lib/mysql
  33. volumes:
  34. - name: db
  35. hostPath:
  36. path: /var/lib/mysql
  37. ---
  38. apiVersion: v1
  39. kind: Service
  40. metadata:
  41. name: mysql
  42. namespace: blog
  43. spec:
  44. selector:
  45. app: mysql
  46. ports:
  47. - name: mysqlport
  48. protocol: TCP
  49. port: 3306
  50. targetPort: dbport
  51. ---
  52. apiVersion: apps/v1beta1
  53. kind: Deployment
  54. metadata:
  55. name: wordpress-deploy
  56. namespace: blog
  57. labels:
  58. app: wordpress
  59. spec:
  60. revisionHistoryLimit: 10
  61. minReadySeconds: 5
  62. strategy:
  63. type: RollingUpdate
  64. rollingUpdate:
  65. maxSurge: 1
  66. maxUnavailable: 1
  67. template:
  68. metadata:
  69. labels:
  70. app: wordpress
  71. spec:
  72. initContainers:
  73. - name: init-db
  74. image: busybox
  75. command: ['sh', '-c', 'until nslookup mysql; do echo waiting for mysql service; sleep 2; done;']
  76. containers:
  77. - name: wordpress
  78. image: wordpress
  79. imagePullPolicy: IfNotPresent
  80. ports:
  81. - containerPort: 80
  82. name: wdport
  83. env:
  84. - name: WORDPRESS_DB_HOST
  85. value: mysql:3306
  86. - name: WORDPRESS_DB_USER
  87. value: wordpress
  88. - name: WORDPRESS_DB_PASSWORD
  89. value: wordpress
  90. livenessProbe:
  91. tcpSocket:
  92. port: 80
  93. initialDelaySeconds: 3
  94. periodSeconds: 3
  95. readinessProbe:
  96. tcpSocket:
  97. port: 80
  98. initialDelaySeconds: 5
  99. periodSeconds: 10
  100. resources:
  101. limits:
  102. cpu: 200m
  103. memory: 200Mi
  104. requests:
  105. cpu: 100m
  106. memory: 100Mi
  107. ---
  108. apiVersion: v1
  109. kind: Service
  110. metadata:
  111. name: wordpress
  112. namespace: blog
  113. spec:
  114. selector:
  115. app: wordpress
  116. type: NodePort
  117. ports:
  118. - name: wordpressport
  119. protocol: TCP
  120. port: 80
  121. nodePort: 32255
  122. targetPort: wdport

我们这里主要是针对的wordpress来做的提高稳定性的方法,如何对mysql提高一些稳定性呢?大家下去可以试一试,我们接下来会和大家讲解mysql这类有状态的应用在Kubernetes当中的使用方法。

最后,我们来把前面我们部署的相关服务全部删掉,重新通过上面的YAML文件来创建:

  1. $ kubectl create -f wordpress-all.yaml
  2. deployment.apps "mysql-deploy" created
  3. service "mysql" created
  4. deployment.apps "wordpress-deploy" created
  5. service "wordpress" created

看看最后能不能得到我们的最终成果呢?